home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2004 #11 / Amiga Plus CD - 2004 - No. 11.iso / AmiSoft / Comm / www / tidy_os4.lha / tidy / src / config.c < prev    next >
C/C++ Source or Header  |  2004-07-25  |  49KB  |  1,602 lines

  1. /*
  2.   config.c -- read config file and manage config properties
  3.   
  4.   (c) 1998-2004 (W3C) MIT, ERCIM, Keio University
  5.   See tidy.h for the copyright notice.
  6.  
  7.   CVS Info :
  8.  
  9.     $Author: hoehrmann $ 
  10.     $Date: 2004/06/03 15:25:05 $ 
  11.     $Revision: 1.72 $ 
  12.  
  13. */
  14.  
  15. /*
  16.   config files associate a property name with a value.
  17.  
  18.   // comments can start at the beginning of a line
  19.   # comments can start at the beginning of a line
  20.   name: short values fit onto one line
  21.   name: a really long value that
  22.    continues on the next line
  23.  
  24.   property names are case insensitive and should be less than
  25.   60 characters in length and must start at the begining of
  26.   the line, as whitespace at the start of a line signifies a
  27.   line continuation.
  28. */
  29.  
  30. #include "config.h"
  31. #include "tidy-int.h"
  32. #include "message.h"
  33. #include "tmbstr.h"
  34. #include "tags.h"
  35.  
  36. #ifdef WINDOWS_OS
  37. #include <io.h>
  38. #else
  39. #include <unistd.h>
  40. #endif
  41.  
  42. #ifdef TIDY_WIN32_MLANG_SUPPORT
  43. #include "win32tc.h"
  44. #endif
  45.  
  46. int CharEncodingId( ctmbstr charenc ); /* returns -1 if not recognized */
  47.  
  48. void InitConfig( TidyDocImpl* doc )
  49. {
  50.     ClearMemory( &doc->config, sizeof(TidyConfigImpl) );
  51.     ResetConfigToDefault( doc );
  52. }
  53.  
  54. void FreeConfig( TidyDocImpl* doc )
  55. {
  56.     ResetConfigToDefault( doc );
  57.     TakeConfigSnapshot( doc );
  58. }
  59.  
  60.  
  61. /* Arrange so index can be cast to enum
  62. */
  63. static const ctmbstr boolPicks[] = 
  64. {
  65.   "no",
  66.   "yes",
  67.   NULL
  68. };
  69.  
  70. static const ctmbstr invBoolPicks[] = 
  71. {
  72.   "yes",
  73.   "no",
  74.   NULL
  75. };
  76.  
  77. static const ctmbstr autoBoolPicks[] = 
  78. {
  79.   "no",
  80.   "yes",
  81.   "auto",
  82.   NULL
  83. };
  84.  
  85. static const ctmbstr repeatAttrPicks[] = 
  86. {
  87.   "keep-first",
  88.   "keep-last",
  89.   NULL
  90. };
  91.  
  92. static const ctmbstr accessPicks[] = 
  93. {
  94.   "0 - Tidy Classic",
  95.   "1 - Priority 1 Checks",
  96.   "2 - Priority 2 Checks",
  97.   "3 - Priority 3 Checks",
  98.   NULL
  99. };
  100.  
  101. static const ctmbstr charEncPicks[] = 
  102. {
  103.   "raw",
  104.   "ascii",
  105.   "latin0",
  106.   "latin1",
  107.   "utf8",
  108. #ifndef NO_NATIVE_ISO2022_SUPPORT
  109.   "iso2022",
  110. #endif
  111.   "mac",
  112.   "win1252",
  113.   "ibm858",
  114.  
  115. #if SUPPORT_UTF16_ENCODINGS
  116.   "utf16le",
  117.   "utf16be",
  118.   "utf16",
  119. #endif
  120.  
  121. #if SUPPORT_ASIAN_ENCODINGS
  122.   "big5",
  123.   "shiftjis",
  124. #endif
  125.  
  126.   NULL
  127. };
  128.  
  129. static const ctmbstr newlinePicks[] = 
  130. {
  131.   "LF",
  132.   "CRLF",
  133.   "CR",
  134.   NULL
  135. };
  136.  
  137. static const ctmbstr doctypePicks[] = 
  138. {
  139.   "omit",
  140.   "auto",
  141.   "strict",
  142.   "transitional",
  143.   "user",
  144.   NULL 
  145. };
  146.  
  147. #define MU TidyMarkup
  148. #define DG TidyDiagnostics
  149. #define PP TidyPrettyPrint
  150. #define CE TidyEncoding
  151. #define MS TidyMiscellaneous
  152.  
  153. #define IN TidyInteger
  154. #define BL TidyBoolean
  155. #define ST TidyString
  156.  
  157. #define XX (TidyConfigCategory)-1
  158. #define XY (TidyOptionType)-1
  159.  
  160. #define DLF DEFAULT_NL_CONFIG
  161.  
  162. /* If Accessibility checks not supported, make config setting read-only */
  163. #if SUPPORT_ACCESSIBILITY_CHECKS
  164. #define ParseAcc ParseInt
  165. #else
  166. #define ParseAcc NULL 
  167. #endif
  168.  
  169. static const TidyOptionImpl option_defs[] =
  170. {
  171.   { TidyUnknownOption,           MS, "unknown!",                    IN, 0,               NULL,              NULL            },
  172.   { TidyIndentSpaces,            PP, "indent-spaces",               IN, 2,               ParseInt,          NULL            },
  173.   { TidyWrapLen,                 PP, "wrap",                        IN, 68,              ParseInt,          NULL            },
  174.   { TidyTabSize,                 PP, "tab-size",                    IN, 8,               ParseInt,          NULL            },
  175.   { TidyCharEncoding,            CE, "char-encoding",               IN, ASCII,           ParseCharEnc,      charEncPicks    },
  176.   { TidyInCharEncoding,          CE, "input-encoding",              IN, LATIN1,          ParseCharEnc,      charEncPicks    },
  177.   { TidyOutCharEncoding,         CE, "output-encoding",             IN, ASCII,           ParseCharEnc,      charEncPicks    },
  178.   { TidyNewline,                 CE, "newline",                     IN, DLF,             ParseNewline,      newlinePicks    },
  179.   { TidyDoctypeMode,             MU, "doctype-mode",                IN, TidyDoctypeAuto, NULL,              doctypePicks    },
  180.   { TidyDoctype,                 MU, "doctype",                     ST, 0,               ParseDocType,      doctypePicks    },
  181.   { TidyDuplicateAttrs,          MU, "repeated-attributes",         IN, TidyKeepLast,    ParseRepeatAttr,   repeatAttrPicks },
  182.   { TidyAltText,                 MU, "alt-text",                    ST, 0,               ParseString,       NULL            },
  183.  
  184.   /* obsolete */
  185.   { TidySlideStyle,              MS, "slide-style",                 ST, 0,               ParseName,         NULL            },
  186.  
  187.   { TidyErrFile,                 MS, "error-file",                  ST, 0,               ParseString,       NULL            },
  188.   { TidyOutFile,                 MS, "output-file",                 ST, 0,               ParseString,       NULL            },
  189.   { TidyWriteBack,               MS, "write-back",                  BL, no,              ParseBool,         boolPicks       },
  190.   { TidyShowMarkup,              PP, "markup",                      BL, yes,             ParseBool,         boolPicks       },
  191.   { TidyShowWarnings,            DG, "show-warnings",               BL, yes,             ParseBool,         boolPicks       },
  192.   { TidyQuiet,                   MS, "quiet",                       BL, no,              ParseBool,         boolPicks       },
  193.   { TidyIndentContent,           PP, "indent",                      IN, TidyNoState,     ParseIndent,       autoBoolPicks   },
  194.   { TidyHideEndTags,             MU, "hide-endtags",                BL, no,              ParseBool,         boolPicks       },
  195.   { TidyXmlTags,                 MU, "input-xml",                   BL, no,              ParseBool,         boolPicks       },
  196.   { TidyXmlOut,                  MU, "output-xml",                  BL, no,              ParseBool,         boolPicks       },
  197.   { TidyXhtmlOut,                MU, "output-xhtml",                BL, no,              ParseBool,         boolPicks       },
  198.   { TidyHtmlOut,                 MU, "output-html",                 BL, no,              ParseBool,         boolPicks       },
  199.   { TidyXmlDecl,                 MU, "add-xml-decl",                BL, no,              ParseBool,         boolPicks       },
  200.   { TidyUpperCaseTags,           MU, "uppercase-tags",              BL, no,              ParseBool,         boolPicks       },
  201.   { TidyUpperCaseAttrs,          MU, "uppercase-attributes",        BL, no,              ParseBool,         boolPicks       },
  202.   { TidyMakeBare,                MU, "bare",                        BL, no,              ParseBool,         boolPicks       },
  203.   { TidyMakeClean,               MU, "clean",                       BL, no,              ParseBool,         boolPicks       },
  204.   { TidyLogicalEmphasis,         MU, "logical-emphasis",            BL, no,              ParseBool,         boolPicks       },
  205.   { TidyDropPropAttrs,           MU, "drop-proprietary-attributes", BL, no,              ParseBool,         boolPicks       },
  206.   { TidyDropFontTags,            MU, "drop-font-tags",              BL, no,              ParseBool,         boolPicks       },
  207.   { TidyDropEmptyParas,          MU, "drop-empty-paras",            BL, yes,             ParseBool,         boolPicks       },
  208.   { TidyFixComments,             MU, "fix-bad-comments",            BL, yes,             ParseBool,         boolPicks       },
  209.   { TidyBreakBeforeBR,           PP, "break-before-br",             BL, no,              ParseBool,         boolPicks       },
  210.  
  211.   /* obsolete */
  212.   { TidyBurstSlides,             PP, "split",                       BL, no,              ParseBool,         boolPicks       },
  213.  
  214.   { TidyNumEntities,             MU, "numeric-entities",            BL, no,              ParseBool,         boolPicks       },
  215.   { TidyQuoteMarks,              MU, "quote-marks",                 BL, no,              ParseBool,         boolPicks       },
  216.   { TidyQuoteNbsp,               MU, "quote-nbsp",                  BL, yes,             ParseBool,         boolPicks       },
  217.   { TidyQuoteAmpersand,          MU, "quote-ampersand",             BL, yes,             ParseBool,         boolPicks       },
  218.   { TidyWrapAttVals,             PP, "wrap-attributes",             BL, no,              ParseBool,         boolPicks       },
  219.   { TidyWrapScriptlets,          PP, "wrap-script-literals",        BL, no,              ParseBool,         boolPicks       },
  220.   { TidyWrapSection,             PP, "wrap-sections",               BL, yes,             ParseBool,         boolPicks       },
  221.   { TidyWrapAsp,                 PP, "wrap-asp",                    BL, yes,             ParseBool,         boolPicks       },
  222.   { TidyWrapJste,                PP, "wrap-jste",                   BL, yes,             ParseBool,         boolPicks       },
  223.   { TidyWrapPhp,                 PP, "wrap-php",                    BL, yes,             ParseBool,         boolPicks       },
  224.   { TidyFixBackslash,            MU, "fix-backslash",               BL, yes,             ParseBool,         boolPicks       },
  225.   { TidyIndentAttributes,        PP, "indent-attributes",           BL, no,              ParseBool,         boolPicks       },
  226.   { TidyXmlPIs,                  MU, "assume-xml-procins",          BL, no,              ParseBool,         boolPicks       },
  227.   { TidyXmlSpace,                MU, "add-xml-space",               BL, no,              ParseBool,         boolPicks       },
  228.   { TidyEncloseBodyText,         MU, "enclose-text",                BL, no,              ParseBool,         boolPicks       },
  229.   { TidyEncloseBlockText,        MU, "enclose-block-text",          BL, no,              ParseBool,         boolPicks       },
  230.   { TidyKeepFileTimes,           MS, "keep-time",                   BL, no,              ParseBool,         boolPicks       },
  231.   { TidyWord2000,                MU, "word-2000",                   BL, no,              ParseBool,         boolPicks       },
  232.   { TidyMark,                    MS, "tidy-mark",                   BL, yes,             ParseBool,         boolPicks       },
  233.   { TidyEmacs,                   MS, "gnu-emacs",                   BL, no,              ParseBool,         boolPicks       },
  234.   { TidyEmacsFile,               MS, "gnu-emacs-file",              ST, 0,               ParseString,       NULL            },
  235.   { TidyLiteralAttribs,          MU, "literal-attributes",          BL, no,              ParseBool,         boolPicks       },
  236.   { TidyBodyOnly,                MU, "show-body-only",              BL, no,              ParseBool,         boolPicks       },
  237.   { TidyFixUri,                  MU, "fix-uri",                     BL, yes,             ParseBool,         boolPicks       },
  238.   { TidyLowerLiterals,           MU, "lower-literals",              BL, yes,             ParseBool,         boolPicks       },
  239.   { TidyHideComments,            MU, "hide-comments",               BL, no,              ParseBool,         boolPicks       },
  240.   { TidyIndentCdata,             MU, "indent-cdata",                BL, no,              ParseBool,         boolPicks       },
  241.   { TidyForceOutput,             MS, "force-output",                BL, no,              ParseBool,         boolPicks       },
  242.   { TidyShowErrors,              DG, "show-errors",                 IN, 6,               ParseInt,          NULL            },
  243.   { TidyAsciiChars,              CE, "ascii-chars",                 BL, yes,             ParseBool,         boolPicks       },
  244.   { TidyJoinClasses,             MU, "join-classes",                BL, no,              ParseBool,         boolPicks       },
  245.   { TidyJoinStyles,              MU, "join-styles",                 BL, yes,             ParseBool,         boolPicks       },
  246.   { TidyEscapeCdata,             MU, "escape-cdata",                BL, no,              ParseBool,         boolPicks       },
  247. #if SUPPORT_ASIAN_ENCODINGS
  248.   { TidyLanguage,                CE, "language",                    ST, 0,               ParseName,         NULL            },
  249.   { TidyNCR,                     MU, "ncr",                         BL, yes,             ParseBool,         boolPicks       },
  250. #endif
  251. #if SUPPORT_UTF16_ENCODINGS
  252.   { TidyOutputBOM,               CE, "output-bom",                  IN, TidyAutoState,   ParseBOM,          autoBoolPicks   },
  253. #endif
  254.   { TidyReplaceColor,            MU, "replace-color",               BL, no,              ParseBool,         boolPicks       },
  255.   { TidyCSSPrefix,               MU, "css-prefix",                  ST, 0,               ParseCSS1Selector, NULL            },
  256.   { TidyInlineTags,              MU, "new-inline-tags",             ST, 0,               ParseTagNames,     NULL            },
  257.   { TidyBlockTags,               MU, "new-blocklevel-tags",         ST, 0,               ParseTagNames,     NULL            },
  258.   { TidyEmptyTags,               MU, "new-empty-tags",              ST, 0,               ParseTagNames,     NULL            },
  259.   { TidyPreTags,                 MU, "new-pre-tags",                ST, 0,               ParseTagNames,     NULL            },
  260.   { TidyAccessibilityCheckLevel, DG, "accessibility-check",         IN, 0,               ParseAcc,          accessPicks     },
  261.   { TidyVertSpace,               PP, "vertical-space",              BL, no,              ParseBool,         boolPicks       },
  262. #if SUPPORT_ASIAN_ENCODINGS
  263.   { TidyPunctWrap,               PP, "punctuation-wrap",            BL, no,              ParseBool,         boolPicks       },
  264. #endif
  265.   { TidyMergeDivs,               MU, "merge-divs",                  BL, yes,             ParseBool,         boolPicks       },
  266.   { N_TIDY_OPTIONS,               XX, "unknown!",                    XY,    0,               NULL,              NULL            }
  267. };
  268.  
  269. /* Should only be called by options set by name
  270. ** thus, it is cheaper to do a few scans than set
  271. ** up every option in a hash table.
  272. */
  273. const TidyOptionImpl* lookupOption( ctmbstr s )
  274. {
  275.     const TidyOptionImpl* np = option_defs;
  276.     for ( /**/; np < option_defs + N_TIDY_OPTIONS; ++np )
  277.     {
  278.         if ( tmbstrcasecmp(s, np->name) == 0 )
  279.             return np;
  280.     }
  281.     return NULL;
  282. }
  283.  
  284. const TidyOptionImpl* getOption( TidyOptionId optId )
  285. {
  286.   if ( optId < N_TIDY_OPTIONS )
  287.       return option_defs + optId;
  288.   return NULL;
  289. }
  290.  
  291.  
  292. static void FreeOptionValue( const TidyOptionImpl* option, ulong value )
  293. {
  294.     if ( value && option->type == TidyString && value != option->dflt )
  295.     {
  296.         MemFree( (void*) value );
  297.     }
  298. }
  299.  
  300. static void CopyOptionValue( const TidyOptionImpl* option,
  301.                              ulong* oldval, ulong newval )
  302. {
  303.     assert( oldval != NULL );
  304.     FreeOptionValue( option, *oldval );
  305.  
  306.     if ( newval && option->type == TidyString && newval != option->dflt )
  307.         *oldval = (ulong) tmbstrdup( (ctmbstr) newval );
  308.     else
  309.         *oldval = newval;
  310. }
  311.  
  312.  
  313. Bool SetOptionValue( TidyDocImpl* doc, TidyOptionId optId, ctmbstr val )
  314. {
  315.    const TidyOptionImpl* option = &option_defs[ optId ];
  316.    Bool status = ( optId < N_TIDY_OPTIONS );
  317.    if ( status )
  318.    {
  319.       assert( option->id == optId && option->type == TidyString );
  320.       FreeOptionValue( option, doc->config.value[ optId ] );
  321.       doc->config.value[ optId ] = (ulong) tmbstrdup( val );
  322.    }
  323.    return status;
  324. }
  325.  
  326. Bool SetOptionInt( TidyDocImpl* doc, TidyOptionId optId, ulong val )
  327. {
  328.    Bool status = ( optId < N_TIDY_OPTIONS );
  329.    if ( status )
  330.    {
  331.        assert( option_defs[ optId ].type == TidyInteger );
  332.        doc->config.value[ optId ] = val;
  333.    }
  334.    return status;
  335. }
  336.  
  337. Bool SetOptionBool( TidyDocImpl* doc, TidyOptionId optId, Bool val )
  338. {
  339.    Bool status = ( optId < N_TIDY_OPTIONS );
  340.    if ( status )
  341.    {
  342.        assert( option_defs[ optId ].type == TidyBoolean );
  343.        doc->config.value[ optId ] = val;
  344.    }
  345.    return status;
  346. }
  347.  
  348. Bool ResetOptionToDefault( TidyDocImpl* doc, TidyOptionId optId )
  349. {
  350.     Bool status = ( optId > 0 && optId < N_TIDY_OPTIONS );
  351.     if ( status )
  352.     {
  353.         const TidyOptionImpl* option = option_defs + optId;
  354.         ulong* value = &doc->config.value[ optId ];
  355.         assert( optId == option->id );
  356.         CopyOptionValue( option, value, option->dflt );
  357.     }
  358.     return status;
  359. }
  360.  
  361. static void ReparseTagType( TidyDocImpl* doc, TidyOptionId optId )
  362. {
  363.     ctmbstr tagdecl = cfgStr( doc, optId );
  364.     tmbstr dupdecl = tmbstrdup( tagdecl );
  365.     ParseConfigValue( doc, optId, dupdecl );
  366.     MemFree( dupdecl );
  367. }
  368.  
  369. /* Not efficient, but effective */
  370. static void ReparseTagDecls( TidyDocImpl* doc )
  371. {
  372.     FreeDeclaredTags( doc, 0 );
  373.     if ( cfg(doc, TidyInlineTags) )
  374.         ReparseTagType( doc, TidyInlineTags );
  375.     if ( cfg(doc, TidyBlockTags) )
  376.         ReparseTagType( doc, TidyBlockTags );
  377.     if ( cfg(doc, TidyEmptyTags) )
  378.         ReparseTagType( doc, TidyEmptyTags );
  379.     if ( cfg(doc, TidyPreTags) )
  380.         ReparseTagType( doc, TidyPreTags );
  381. }
  382.  
  383. void ResetConfigToDefault( TidyDocImpl* doc )
  384. {
  385.     uint ixVal;
  386.     const TidyOptionImpl* option = option_defs;
  387.     ulong* value = &doc->config.value[ 0 ];
  388.     for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal )
  389.     {
  390.         assert( ixVal == (uint) option->id );
  391.         CopyOptionValue( option, &value[ixVal], option->dflt );
  392.     }
  393.     FreeDeclaredTags( doc, 0 );
  394. }
  395.  
  396. void TakeConfigSnapshot( TidyDocImpl* doc )
  397. {
  398.     uint ixVal;
  399.     const TidyOptionImpl* option = option_defs;
  400.     ulong* value = &doc->config.value[ 0 ];
  401.     ulong* snap  = &doc->config.snapshot[ 0 ];
  402.  
  403.     AdjustConfig( doc );  /* Make sure it's consistent */
  404.     for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal )
  405.     {
  406.         assert( ixVal == (uint) option->id );
  407.         CopyOptionValue( option, &snap[ixVal], value[ixVal] );
  408.     }
  409. }
  410.  
  411. void ResetConfigToSnapshot( TidyDocImpl* doc )
  412. {
  413.     uint ixVal;
  414.     const TidyOptionImpl* option = option_defs;
  415.     ulong* value = &doc->config.value[ 0 ];
  416.     ulong* snap  = &doc->config.snapshot[ 0 ];
  417.  
  418.     for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal )
  419.     {
  420.         assert( ixVal == (uint) option->id );
  421.         CopyOptionValue( option, &value[ixVal], snap[ixVal] );
  422.     }
  423.     FreeDeclaredTags( doc, 0 );
  424.     ReparseTagDecls( doc );
  425. }
  426.  
  427. void CopyConfig( TidyDocImpl* docTo, TidyDocImpl* docFrom )
  428. {
  429.     if ( docTo != docFrom )
  430.     {
  431.         uint ixVal;
  432.         const TidyOptionImpl* option = option_defs;
  433.         ulong* from = &docFrom->config.value[ 0 ];
  434.         ulong* to   = &docTo->config.value[ 0 ];
  435.  
  436.         TakeConfigSnapshot( docTo );
  437.         for ( ixVal=0; ixVal < N_TIDY_OPTIONS; ++option, ++ixVal )
  438.         {
  439.             assert( ixVal == (uint) option->id );
  440.             CopyOptionValue( option, &to[ixVal], from[ixVal] );
  441.         }
  442.         ReparseTagDecls( docTo );
  443.         AdjustConfig( docTo );  /* Make sure it's consistent */
  444.     }
  445. }
  446.  
  447.  
  448. #ifdef _DEBUG
  449.  
  450. /* Debug accessor functions will be type-safe and assert option type match */
  451. ulong   _cfgGet( TidyDocImpl* doc, TidyOptionId optId )
  452. {
  453.   assert( optId < N_TIDY_OPTIONS );
  454.   return doc->config.value[ optId ];
  455. }
  456.  
  457. Bool    _cfgGetBool( TidyDocImpl* doc, TidyOptionId optId )
  458. {
  459.   uint val = _cfgGet( doc, optId );
  460.   const TidyOptionImpl* opt = &option_defs[ optId ];
  461.   assert( opt && opt->type == TidyBoolean );
  462.   return (Bool) val;
  463. }
  464.  
  465. ctmbstr _cfgGetString( TidyDocImpl* doc, TidyOptionId optId )
  466. {
  467.   uint val = _cfgGet( doc, optId );
  468.   const TidyOptionImpl* opt = &option_defs[ optId ];
  469.   assert( opt && opt->type == TidyString );
  470.   return (ctmbstr) val;
  471. }
  472. #endif
  473.  
  474.  
  475. /* for use with Gnu Emacs */
  476. void SetEmacsFilename( TidyDocImpl* doc, ctmbstr filename )
  477. {
  478.     SetOptionValue( doc, TidyEmacsFile, filename );
  479. }
  480.  
  481.  
  482. static tchar GetC( TidyConfigImpl* config )
  483. {
  484.     if ( config->cfgIn )
  485.         return ReadChar( config->cfgIn );
  486.     return EndOfStream;
  487. }
  488.  
  489. static tchar FirstChar( TidyConfigImpl* config )
  490. {
  491.     config->c = GetC( config );
  492.     return config->c;
  493. }
  494.  
  495. static tchar AdvanceChar( TidyConfigImpl* config )
  496. {
  497.     if ( config->c != EndOfStream )
  498.         config->c = GetC( config );
  499.     return config->c;
  500. }
  501.  
  502. static tchar SkipWhite( TidyConfigImpl* config )
  503. {
  504.     while ( IsWhite(config->c) && !IsNewline(config->c) )
  505.         config->c = GetC( config );
  506.     return config->c;
  507. }
  508.  
  509. /* skip until end of line
  510. static tchar SkipToEndofLine( TidyConfigImpl* config )
  511. {
  512.     while ( config->c != EndOfStream )
  513.     {
  514.         config->c = GetC( config );
  515.         if ( config->c == '\n' || config->c == '\r' )
  516.             break;
  517.     }
  518.     return config->c;
  519. }
  520. */
  521.  
  522. /*
  523.  skip over line continuations
  524.  to start of next property
  525. */
  526. static uint NextProperty( TidyConfigImpl* config )
  527. {
  528.     do
  529.     {
  530.         /* skip to end of line */
  531.         while ( config->c != '\n' &&  config->c != '\r' &&  config->c != EndOfStream )
  532.              config->c = GetC( config );
  533.  
  534.         /* treat  \r\n   \r  or  \n as line ends */
  535.         if ( config->c == '\r' )
  536.              config->c = GetC( config );
  537.  
  538.         if ( config->c == '\n' )
  539.             config->c = GetC( config );
  540.     }
  541.     while ( IsWhite(config->c) );  /* line continuation? */
  542.  
  543.     return config->c;
  544. }
  545.  
  546. /*
  547.  Todd Lewis contributed this code for expanding
  548.  ~/foo or ~your/foo according to $HOME and your
  549.  user name. This will work partially on any system 
  550.  which defines $HOME.  Support for ~user/foo will
  551.  work on systems that support getpwnam(userid), 
  552.  namely Unix/Linux.
  553. */
  554. ctmbstr ExpandTilde( ctmbstr filename )
  555. {
  556.     char *home_dir = NULL;
  557.  
  558.     if ( !filename )
  559.         return NULL;
  560.  
  561.     if ( filename[0] != '~' )
  562.         return filename;
  563.  
  564.     if (filename[1] == '/')
  565.     {
  566.         home_dir = getenv("HOME");
  567.         if ( home_dir )
  568.             ++filename;
  569.     }
  570. #ifdef SUPPORT_GETPWNAM
  571.     else
  572.     {
  573.         struct passwd *passwd = NULL;
  574.         ctmbstr s = filename + 1;
  575.         tmbstr t;
  576.  
  577.         while ( *s && *s != '/' )
  578.             s++;
  579.  
  580.         if ( t = MemAlloc(s - filename) )
  581.         {
  582.             memcpy(t, filename+1, s-filename-1);
  583.             t[s-filename-1] = 0;
  584.  
  585.             passwd = getpwnam(t);
  586.  
  587.             MemFree(t);
  588.         }
  589.  
  590.         if ( passwd )
  591.         {
  592.             filename = s;
  593.             home_dir = passwd->pw_dir;
  594.         }
  595.     }
  596. #endif /* SUPPORT_GETPWNAM */
  597.  
  598.     if ( home_dir )
  599.     {
  600.         uint len = tmbstrlen(filename) + tmbstrlen(home_dir) + 1;
  601.         tmbstr p = MemAlloc( len );
  602.         tmbstrcpy( p, home_dir );
  603.         tmbstrcat( p, filename );
  604.         return (ctmbstr) p;
  605.     }
  606.     return (ctmbstr) filename;
  607. }
  608.  
  609. Bool tidyFileExists( ctmbstr filename )
  610. {
  611.   ctmbstr fname = (tmbstr) ExpandTilde( filename );
  612. #ifndef NO_ACCESS_SUPPORT
  613.   Bool exists = ( access(fname, 0) == 0 );
  614. #else
  615.   Bool exists;
  616.   /* at present */
  617.   FILE* fin = fopen(fname, "r");
  618.   if (fin != NULL)
  619.       fclose(fin);
  620.   exists = ( fin != NULL );
  621. #endif
  622.   if ( fname != filename )
  623.       MemFree( (tmbstr) fname );
  624.   return exists;
  625. }
  626.  
  627.  
  628. #ifndef TIDY_MAX_NAME
  629. #define TIDY_MAX_NAME 64
  630. #endif
  631.  
  632. int ParseConfigFile( TidyDocImpl* doc, ctmbstr file )
  633. {
  634.     return ParseConfigFileEnc( doc, file, "ascii" );
  635. }
  636.  
  637. /* open the file and parse its contents
  638. */
  639. int ParseConfigFileEnc( TidyDocImpl* doc, ctmbstr file, ctmbstr charenc )
  640. {
  641.     uint opterrs = doc->optionErrors;
  642.     tmbstr fname = (tmbstr) ExpandTilde( file );
  643.     TidyConfigImpl* cfg = &doc->config;
  644.     FILE* fin = fopen( fname, "r" );
  645.     int enc = CharEncodingId( charenc );
  646.  
  647.     if ( fin == NULL || enc < 0 )
  648.         FileError( doc, fname, TidyConfig );
  649.     else
  650.     {
  651.         tchar c;
  652.         cfg->cfgIn = FileInput( doc, fin, enc );
  653.         c = FirstChar( cfg );
  654.        
  655.         for ( c = SkipWhite(cfg); c != EndOfStream; c = NextProperty(cfg) )
  656.         {
  657.             uint ix = 0;
  658.             tmbchar name[ TIDY_MAX_NAME ] = {0};
  659.  
  660.             /* // or # start a comment */
  661.             if ( c == '/' || c == '#' )
  662.                 continue;
  663.  
  664.             while ( ix < sizeof(name)-1 && c != '\n' && c != EndOfStream && c != ':' )
  665.             {
  666.                 name[ ix++ ] = (tmbchar) c;  /* Option names all ASCII */
  667.                 c = AdvanceChar( cfg );
  668.             }
  669.  
  670.             if ( c == ':' )
  671.             {
  672.                 const TidyOptionImpl* option = lookupOption( name );
  673.                 c = AdvanceChar( cfg );
  674.                 if ( option )
  675.                     option->parser( doc, option );
  676.                 else
  677.                 {
  678.                     if (NULL != doc->pOptCallback)
  679.                     {
  680.                         TidyConfigImpl* cfg = &doc->config;
  681.                         tmbchar buf[8192];
  682.                         int i = 0;
  683.                         tchar delim = 0;
  684.                         Bool waswhite = yes;
  685.  
  686.                         tchar c = SkipWhite( cfg );
  687.  
  688.                         if ( c == '"' || c == '\'' )
  689.                         {
  690.                             delim = c;
  691.                             c = AdvanceChar( cfg );
  692.                         }
  693.  
  694.                         while ( i < sizeof(buf)-2 && c != EndOfStream && c != '\r' && c != '\n' )
  695.                         {
  696.                             if ( delim && c == delim )
  697.                                 break;
  698.  
  699.                             if ( IsWhite(c) )
  700.                             {
  701.                                 if ( waswhite )
  702.                                 {
  703.                                     c = AdvanceChar( cfg );
  704.                                     continue;
  705.                                 }
  706.                                 c = ' ';
  707.                             }
  708.                             else
  709.                                 waswhite = no;
  710.  
  711.                             buf[i++] = (tmbchar) c;
  712.                             c = AdvanceChar( cfg );
  713.                         }
  714.                         buf[i] = '\0';
  715.                         if (no == (*doc->pOptCallback)( name, buf ))
  716.                             ReportUnknownOption( doc, name );
  717.                     }
  718.                     else
  719.                         ReportUnknownOption( doc, name );
  720.                 }
  721.             }
  722.         }
  723.  
  724.         fclose( fin );
  725.         MemFree( (void *)cfg->cfgIn->source.sourceData ); /* fix for bug #810259 */
  726.         MemFree( cfg->cfgIn );
  727.         cfg->cfgIn = NULL;
  728.     }
  729.  
  730.     if ( fname != (tmbstr) file )
  731.         MemFree( fname );
  732.  
  733.     AdjustConfig( doc );
  734.  
  735.     /* any new config errors? If so, return warning status. */
  736.     return (doc->optionErrors > opterrs ? 1 : 0); 
  737. }
  738.  
  739. /* returns false if unknown option, missing parameter,
  740. ** or option doesn't use parameter
  741. */
  742. Bool ParseConfigOption( TidyDocImpl* doc, ctmbstr optnam, ctmbstr optval )
  743. {
  744.     const TidyOptionImpl* option = lookupOption( optnam );
  745.     Bool status = ( option != NULL );
  746.     if ( !status )
  747.     {
  748.         /* Not a standard tidy option.  Check to see if the user application 
  749.            recognizes it  */
  750.         if (NULL != doc->pOptCallback)
  751.             status = (*doc->pOptCallback)( optnam, optval );
  752.         if (!status)
  753.             ReportUnknownOption( doc, optnam );
  754.     }
  755.     else 
  756.         status = ParseConfigValue( doc, option->id, optval );
  757.     return status;
  758. }
  759.  
  760. /* returns false if unknown option, missing parameter,
  761. ** or option doesn't use parameter
  762. */
  763. Bool ParseConfigValue( TidyDocImpl* doc, TidyOptionId optId, ctmbstr optval )
  764. {
  765.     const TidyOptionImpl* option = option_defs + optId;
  766.     Bool status = ( optId < N_TIDY_OPTIONS && optval != NULL );
  767.  
  768.     if ( !status )
  769.         ReportBadArgument( doc, option->name );
  770.     else
  771.     {
  772.         TidyBuffer inbuf = {0};            /* Set up input source */
  773.         tidyBufAttach( &inbuf, (byte*)optval, tmbstrlen(optval)+1 );
  774.         doc->config.cfgIn = BufferInput( doc, &inbuf, ASCII );
  775.         doc->config.c = GetC( &doc->config );
  776.  
  777.         status = option->parser( doc, option );
  778.  
  779.         freeStreamIn(doc->config.cfgIn);  /* Release input source */
  780.         doc->config.cfgIn  = NULL;
  781.         tidyBufDetach( &inbuf );
  782.     }
  783.     return status;
  784. }
  785.  
  786.  
  787. /* ensure that char encodings are self consistent */
  788. Bool  AdjustCharEncoding( TidyDocImpl* doc, int encoding )
  789. {
  790.     int outenc = -1;
  791.     int inenc = -1;
  792.     
  793.     switch( encoding )
  794.     {
  795.     case MACROMAN:
  796.         inenc = MACROMAN;
  797.         outenc = ASCII;
  798.         break;
  799.  
  800.     case WIN1252:
  801.         inenc = WIN1252;
  802.         outenc = ASCII;
  803.         break;
  804.  
  805.     case IBM858:
  806.         inenc = IBM858;
  807.         outenc = ASCII;
  808.         break;
  809.  
  810.     case ASCII:
  811.         inenc = LATIN1;
  812.         outenc = ASCII;
  813.         break;
  814.  
  815.     case LATIN0:
  816.         inenc = LATIN0;
  817.         outenc = ASCII;
  818.         break;
  819.  
  820.     case RAW:
  821.     case LATIN1:
  822.     case UTF8:
  823. #ifndef NO_NATIVE_ISO2022_SUPPORT
  824.     case ISO2022:
  825. #endif
  826.  
  827. #if SUPPORT_UTF16_ENCODINGS
  828.     case UTF16LE:
  829.     case UTF16BE:
  830.     case UTF16:
  831. #endif
  832. #if SUPPORT_ASIAN_ENCODINGS
  833.     case SHIFTJIS:
  834.     case BIG5:
  835. #endif
  836.         inenc = outenc = encoding;
  837.         break;
  838.     }
  839.  
  840.     if ( inenc >= 0 )
  841.     {
  842.         SetOptionInt( doc, TidyCharEncoding, encoding );
  843.         SetOptionInt( doc, TidyInCharEncoding, inenc );
  844.         SetOptionInt( doc, TidyOutCharEncoding, outenc );
  845.         return yes;
  846.     }
  847.     return no;
  848. }
  849.  
  850. /* ensure that config is self consistent */
  851. void AdjustConfig( TidyDocImpl* doc )
  852. {
  853.     if ( cfgBool(doc, TidyEncloseBlockText) )
  854.         SetOptionBool( doc, TidyEncloseBodyText, yes );
  855.  
  856.     if ( !cfg(doc, TidyIndentContent) )
  857.         SetOptionInt( doc, TidyIndentSpaces, 0 );
  858.  
  859.     /* disable wrapping */
  860.     if ( cfg(doc, TidyWrapLen) == 0 )
  861.         SetOptionInt( doc, TidyWrapLen, 0x7FFFFFFF );
  862.  
  863.     /* Word 2000 needs o:p to be declared as inline */
  864.     if ( cfgBool(doc, TidyWord2000) )
  865.     {
  866.         doc->config.defined_tags |= tagtype_inline;
  867.         DefineTag( doc, tagtype_inline, "o:p" );
  868.     }
  869.  
  870.     /* #480701 disable XHTML output flag if both output-xhtml and xml input are set */
  871.     if ( cfgBool(doc, TidyXmlTags) )
  872.         SetOptionBool( doc, TidyXhtmlOut, no );
  873.  
  874.     /* XHTML is written in lower case */
  875.     if ( cfgBool(doc, TidyXhtmlOut) )
  876.     {
  877.         SetOptionBool( doc, TidyXmlOut, yes );
  878.         SetOptionBool( doc, TidyUpperCaseTags, no );
  879.         SetOptionBool( doc, TidyUpperCaseAttrs, no );
  880.         /* SetOptionBool( doc, TidyXmlPIs, yes ); */
  881.     }
  882.  
  883.     /* if XML in, then XML out */
  884.     if ( cfgBool(doc, TidyXmlTags) )
  885.     {
  886.         SetOptionBool( doc, TidyXmlOut, yes );
  887.         SetOptionBool( doc, TidyXmlPIs, yes );
  888.     }
  889.  
  890.     /* #427837 - fix by Dave Raggett 02 Jun 01
  891.     ** generate <?xml version="1.0" encoding="iso-8859-1"?>
  892.     ** if the output character encoding is Latin-1 etc.
  893.     */
  894.     if ( cfg(doc, TidyOutCharEncoding) != ASCII &&
  895.          cfg(doc, TidyOutCharEncoding) != UTF8 &&
  896. #if SUPPORT_UTF16_ENCODINGS
  897.          cfg(doc, TidyOutCharEncoding) != UTF16 &&
  898.          cfg(doc, TidyOutCharEncoding) != UTF16BE &&
  899.          cfg(doc, TidyOutCharEncoding) != UTF16LE &&
  900. #endif
  901.          cfgBool(doc, TidyXmlOut) )
  902.     {
  903.         SetOptionBool( doc, TidyXmlDecl, yes );
  904.     }
  905.  
  906.     /* XML requires end tags */
  907.     if ( cfgBool(doc, TidyXmlOut) )
  908.     {
  909. #if SUPPORT_UTF16_ENCODINGS
  910.         /* XML requires a BOM on output if using UTF-16 encoding */
  911.         ulong enc = cfg( doc, TidyOutCharEncoding );
  912.         if ( enc == UTF16LE || enc == UTF16BE || enc == UTF16 )
  913.             SetOptionInt( doc, TidyOutputBOM, yes );
  914. #endif
  915.         SetOptionBool( doc, TidyQuoteAmpersand, yes );
  916.         SetOptionBool( doc, TidyHideEndTags, no );
  917.     }
  918. }
  919.  
  920. /* unsigned integers */
  921. Bool ParseInt( TidyDocImpl* doc, const TidyOptionImpl* entry )
  922. {
  923.     ulong number = 0;
  924.     Bool digits = no;
  925.     TidyConfigImpl* cfg = &doc->config;
  926.     tchar c = SkipWhite( cfg );
  927.  
  928.     while ( IsDigit(c) )
  929.     {
  930.         number = c - '0' + (10 * number);
  931.         digits = yes;
  932.         c = AdvanceChar( cfg );
  933.     }
  934.  
  935.     if ( !digits )
  936.         ReportBadArgument( doc, entry->name );
  937.     else
  938.         SetOptionInt( doc, entry->id, number );
  939.     return digits;
  940. }
  941.  
  942. /* true/false or yes/no or 0/1 or "auto" only looks at 1st char */
  943. static Bool ParseTriState( TidyTriState theState, TidyDocImpl* doc,
  944.                     const TidyOptionImpl* entry, ulong* flag )
  945. {
  946.     TidyConfigImpl* cfg = &doc->config;
  947.     tchar c = SkipWhite( cfg );
  948.  
  949.     if (c == 't' || c == 'T' || c == 'y' || c == 'Y' || c == '1')
  950.         *flag = yes;
  951.     else if (c == 'f' || c == 'F' || c == 'n' || c == 'N' || c == '0')
  952.         *flag = no;
  953.     else if (theState == TidyAutoState && (c == 'a' || c =='A'))
  954.         *flag = TidyAutoState;
  955.     else
  956.     {
  957.         ReportBadArgument( doc, entry->name );
  958.         return no;
  959.     }
  960.  
  961.     return yes;
  962. }
  963.  
  964. /* cr, lf or crlf */
  965. Bool ParseNewline( TidyDocImpl* doc, const TidyOptionImpl* entry )
  966. {
  967.     int nl = -1;
  968.     tmbchar work[ 16 ] = {0};
  969.     tmbstr cp = work, end = work + sizeof(work);
  970.     TidyConfigImpl* cfg = &doc->config;
  971.     tchar c = SkipWhite( cfg );
  972.  
  973.     while ( c!=EndOfStream && cp < end && !IsWhite(c) && c != '\r' && c != '\n' )
  974.     {
  975.         *cp++ = (tmbchar) c;
  976.         c = AdvanceChar( cfg );
  977.     }
  978.     *cp = 0;
  979.  
  980.     if ( tmbstrcasecmp(work, "lf") == 0 )
  981.         nl = TidyLF;
  982.     else if ( tmbstrcasecmp(work, "crlf") == 0 )
  983.         nl = TidyCRLF;
  984.     else if ( tmbstrcasecmp(work, "cr") == 0 )
  985.         nl = TidyCR;
  986.  
  987.     if ( nl < TidyLF || nl > TidyCR )
  988.         ReportBadArgument( doc, entry->name );
  989.     else
  990.         SetOptionInt( doc, entry->id, nl );
  991.     return ( nl >= TidyLF && nl <= TidyCR );
  992. }
  993.  
  994. Bool ParseBool( TidyDocImpl* doc, const TidyOptionImpl* entry )
  995. {
  996.     ulong flag = 0;
  997.     Bool status = ParseTriState( TidyNoState, doc, entry, &flag );
  998.     if ( status )
  999.         SetOptionBool( doc, entry->id, (Bool) flag );
  1000.     return status;
  1001. }
  1002.  
  1003. /* a string excluding whitespace */
  1004. Bool ParseName( TidyDocImpl* doc, const TidyOptionImpl* option )
  1005. {
  1006.     tmbchar buf[ 1024 ] = {0};
  1007.     int i = 0;
  1008.     uint c = SkipWhite( &doc->config );
  1009.  
  1010.     while ( i < sizeof(buf)-2 && c != EndOfStream && !IsWhite(c) )
  1011.     {
  1012.         buf[i++] = (tmbchar) c;
  1013.         c = AdvanceChar( &doc->config );
  1014.     }
  1015.     buf[i] = 0;
  1016.  
  1017.     if ( i == 0 )
  1018.         ReportBadArgument( doc, option->name );
  1019.     else
  1020.         SetOptionValue( doc, option->id, buf );
  1021.     return ( i > 0 );
  1022. }
  1023.  
  1024. /* #508936 - CSS class naming for -clean option */
  1025. Bool ParseCSS1Selector( TidyDocImpl* doc, const TidyOptionImpl* option )
  1026. {
  1027.     char buf[256] = {0};
  1028.     int i = 0;
  1029.     uint c = SkipWhite( &doc->config );
  1030.  
  1031.     while ( i < sizeof(buf)-2 && c != EndOfStream && !IsWhite(c) )
  1032.     {
  1033.         buf[i++] = (tmbchar) c;
  1034.         c = AdvanceChar( &doc->config );
  1035.     }
  1036.     buf[i] = '\0';
  1037.  
  1038.     if ( i == 0 || !IsCSS1Selector(buf) ) {
  1039.         ReportBadArgument( doc, option->name );
  1040.         return no;
  1041.     }
  1042.  
  1043.     buf[i++] = '-';  /* Make sure any escaped Unicode is terminated */
  1044.     buf[i] = 0;      /* so valid class names are generated after */
  1045.                      /* Tidy appends last digits. */
  1046.  
  1047.     SetOptionValue( doc, option->id, buf );
  1048.     return yes;
  1049. }
  1050.  
  1051. /* Coordinates Config update and Tags data */
  1052. static void DeclareUserTag( TidyDocImpl* doc, TidyOptionId optId, int tagType, tmbstr name )
  1053. {
  1054.   ctmbstr prvval = cfgStr( doc, optId );
  1055.   tmbstr catval = name;
  1056.   if ( prvval )
  1057.   {
  1058.     uint len = tmbstrlen(name) + tmbstrlen(prvval) + 3;
  1059.     catval = tmbstrndup( prvval, len );
  1060.     tmbstrcat( catval, ", " );
  1061.     tmbstrcat( catval, name );
  1062.   }
  1063.   DefineTag( doc, tagType, name );
  1064.   SetOptionValue( doc, optId, catval );
  1065.   if ( prvval )
  1066.     MemFree( catval );
  1067. }
  1068.  
  1069. /* a space or comma separated list of tag names */
  1070. Bool ParseTagNames( TidyDocImpl* doc, const TidyOptionImpl* option )
  1071. {
  1072.     TidyConfigImpl* cfg = &doc->config;
  1073.     tmbchar buf[1024];
  1074.     int i = 0, nTags = 0;
  1075.     uint c = SkipWhite( cfg );
  1076.     uint ttyp = 0;
  1077.  
  1078.     switch ( option->id )
  1079.     {
  1080.     case TidyInlineTags:  ttyp = tagtype_inline;    break;
  1081.     case TidyBlockTags:   ttyp = tagtype_block;     break;
  1082.     case TidyEmptyTags:   ttyp = tagtype_empty;     break;
  1083.     case TidyPreTags:     ttyp = tagtype_pre;       break;
  1084.     default:
  1085.        ReportUnknownOption( doc, option->name );
  1086.        return no;
  1087.     }
  1088.  
  1089.     SetOptionValue( doc, option->id, NULL );
  1090.     FreeDeclaredTags( doc, ttyp );
  1091.     cfg->defined_tags |= ttyp;
  1092.  
  1093.     do
  1094.     {
  1095.         if (c == ' ' || c == '\t' || c == ',')
  1096.         {
  1097.             c = AdvanceChar( cfg );
  1098.             continue;
  1099.         }
  1100.  
  1101.         if ( c == '\r' || c == '\n' )
  1102.         {
  1103.             uint c2 = AdvanceChar( cfg );
  1104.             if ( c == '\r' && c2 == '\n' )
  1105.                 c = AdvanceChar( cfg );
  1106.             else
  1107.                 c = c2;
  1108.  
  1109.             if ( !IsWhite(c) )
  1110.             {
  1111.                 buf[i] = 0;
  1112.                 UngetChar( c, cfg->cfgIn );
  1113.                 UngetChar( '\n', cfg->cfgIn );
  1114.                 break;
  1115.             }
  1116.         }
  1117.  
  1118.         /*
  1119.         if ( c == '\n' )
  1120.         {
  1121.             c = AdvanceChar( cfg );
  1122.             if ( !IsWhite(c) )
  1123.             {
  1124.                 buf[i] = 0;
  1125.                 UngetChar( c, cfg->cfgIn );
  1126.                 UngetChar( '\n', cfg->cfgIn );
  1127.                 break;
  1128.             }
  1129.         }
  1130.         */
  1131.  
  1132.         while ( i < sizeof(buf)-2 && c != EndOfStream && !IsWhite(c) && c != ',' )
  1133.         {
  1134.             buf[i++] = (tmbchar) c;
  1135.             c = AdvanceChar( cfg );
  1136.         }
  1137.  
  1138.         buf[i] = '\0';
  1139.         if (i == 0)          /* Skip empty tag definition.  Possible when */
  1140.             continue;        /* there is a trailing space on the line. */
  1141.             
  1142.         /* add tag to dictionary */
  1143.         DeclareUserTag( doc, option->id, ttyp, buf );
  1144.         i = 0;
  1145.         ++nTags;
  1146.     }
  1147.     while ( c != EndOfStream );
  1148.  
  1149.     if ( i > 0 )
  1150.       DeclareUserTag( doc, option->id, ttyp, buf );
  1151.     return ( nTags > 0 );
  1152. }
  1153.  
  1154. /* a string including whitespace */
  1155. /* munges whitespace sequences */
  1156.  
  1157. Bool ParseString( TidyDocImpl* doc, const TidyOptionImpl* option )
  1158. {
  1159.     TidyConfigImpl* cfg = &doc->config;
  1160.     tmbchar buf[8192];
  1161.     int i = 0;
  1162.     tchar delim = 0;
  1163.     Bool waswhite = yes;
  1164.  
  1165.     tchar c = SkipWhite( cfg );
  1166.  
  1167.     if ( c == '"' || c == '\'' )
  1168.     {
  1169.         delim = c;
  1170.         c = AdvanceChar( cfg );
  1171.     }
  1172.  
  1173.     while ( i < sizeof(buf)-2 && c != EndOfStream && c != '\r' && c != '\n' )
  1174.     {
  1175.         if ( delim && c == delim )
  1176.             break;
  1177.  
  1178.         if ( IsWhite(c) )
  1179.         {
  1180.             if ( waswhite )
  1181.             {
  1182.                 c = AdvanceChar( cfg );
  1183.                 continue;
  1184.             }
  1185.             c = ' ';
  1186.         }
  1187.         else
  1188.             waswhite = no;
  1189.  
  1190.         buf[i++] = (tmbchar) c;
  1191.         c = AdvanceChar( cfg );
  1192.     }
  1193.     buf[i] = '\0';
  1194.  
  1195.     SetOptionValue( doc, option->id, buf );
  1196.     return yes;
  1197. }
  1198.  
  1199. Bool ParseCharEnc( TidyDocImpl* doc, const TidyOptionImpl* option )
  1200. {
  1201.     tmbchar buf[64] = {0};
  1202.     int i = 0, enc = ASCII;
  1203.     Bool validEncoding = yes;
  1204.     tchar c = SkipWhite( &doc->config );
  1205.  
  1206.     while ( i < sizeof(buf)-2 && c != EndOfStream && !IsWhite(c) )
  1207.     {
  1208.         buf[i++] = (tmbchar) ToLower( c );
  1209.         c = AdvanceChar( &doc->config );
  1210.     }
  1211.     buf[i] = 0;
  1212.  
  1213.     enc = CharEncodingId( buf );
  1214.  
  1215. #ifdef TIDY_WIN32_MLANG_SUPPORT
  1216.     /* limit support to --input-encoding */
  1217.     if (option->id != TidyInCharEncoding && enc > WIN32MLANG)
  1218.         enc = -1;
  1219. #endif
  1220.  
  1221.     if ( enc < 0 )
  1222.     {
  1223.         validEncoding = no;
  1224.         ReportBadArgument( doc, option->name );
  1225.     }
  1226.     else
  1227.         SetOptionInt( doc, option->id, enc );
  1228.  
  1229.     if ( validEncoding && option->id == TidyCharEncoding )
  1230.         AdjustCharEncoding( doc, enc );
  1231.     return validEncoding;
  1232. }
  1233.  
  1234.  
  1235. int CharEncodingId( ctmbstr charenc )
  1236. {
  1237.     int enc = -1;
  1238.     if ( tmbstrcasecmp(charenc, "ascii") == 0 )
  1239.         enc = ASCII;
  1240.     else if ( tmbstrcasecmp(charenc, "latin0") == 0 )
  1241.         enc = LATIN0;
  1242.     else if ( tmbstrcasecmp(charenc, "latin1") == 0 )
  1243.         enc = LATIN1;
  1244.     else if ( tmbstrcasecmp(charenc, "raw") == 0 )
  1245.         enc = RAW;
  1246.     else if ( tmbstrcasecmp(charenc, "utf8") == 0 )
  1247.         enc = UTF8;
  1248. #ifndef NO_NATIVE_ISO2022_SUPPORT
  1249.     else if ( tmbstrcasecmp(charenc, "iso2022") == 0 )
  1250.         enc = ISO2022;
  1251. #endif
  1252.     else if ( tmbstrcasecmp(charenc, "mac") == 0 )
  1253.         enc = MACROMAN;
  1254.     else if ( tmbstrcasecmp(charenc, "win1252") == 0 )
  1255.         enc = WIN1252;
  1256.     else if ( tmbstrcasecmp(charenc, "ibm858") == 0 )
  1257.         enc = IBM858;
  1258.  
  1259. #if SUPPORT_UTF16_ENCODINGS
  1260.     else if ( tmbstrcasecmp(charenc, "utf16le") == 0 )
  1261.         enc = UTF16LE;
  1262.     else if ( tmbstrcasecmp(charenc, "utf16be") == 0 )
  1263.         enc = UTF16BE;
  1264.     else if ( tmbstrcasecmp(charenc, "utf16") == 0 )
  1265.         enc = UTF16;
  1266. #endif
  1267.  
  1268. #if SUPPORT_ASIAN_ENCODINGS
  1269.     else if ( tmbstrcasecmp(charenc, "big5") == 0 )
  1270.         enc = BIG5;
  1271.     else if ( tmbstrcasecmp(charenc, "shiftjis") == 0 )
  1272.         enc = SHIFTJIS;
  1273. #endif
  1274.  
  1275. #ifdef TIDY_WIN32_MLANG_SUPPORT
  1276.     else
  1277.     {
  1278.         uint wincp = Win32MLangGetCPFromName(charenc);
  1279.         if (wincp)
  1280.             enc = wincp;
  1281.     }
  1282. #endif
  1283.  
  1284.     return enc;
  1285. }
  1286.  
  1287. ctmbstr CharEncodingName( int encoding )
  1288. {
  1289.     ctmbstr encodingName = GetEncodingNameFromTidyId(encoding);
  1290.  
  1291.     if (!encodingName)
  1292.         encodingName = "unknown";
  1293.  
  1294.     return encodingName;
  1295. }
  1296.  
  1297. Bool ParseIndent( TidyDocImpl* doc, const TidyOptionImpl* option )
  1298. {
  1299.     ulong flag = 0;
  1300.     Bool status = ParseTriState( TidyAutoState, doc, option, &flag );
  1301.  
  1302.     if ( status )
  1303.     {
  1304.         SetOptionInt( doc, TidyIndentContent, flag );
  1305.     }
  1306.     return status;
  1307. }
  1308.  
  1309. /*
  1310.    doctype: omit | auto | strict | loose | <fpi>
  1311.  
  1312.    where the fpi is a string similar to
  1313.  
  1314.       "-//ACME//DTD HTML 3.14159//EN"
  1315. */
  1316. Bool ParseDocType( TidyDocImpl* doc, const TidyOptionImpl* option )
  1317. {
  1318.     tmbchar buf[ 32 ] = {0};
  1319.     int i = 0;
  1320.     Bool status = yes;
  1321.     TidyDoctypeModes dtmode = TidyDoctypeAuto;
  1322.  
  1323.     TidyConfigImpl* cfg = &doc->config;
  1324.     tchar c = SkipWhite( cfg );
  1325.  
  1326.     /* "-//ACME//DTD HTML 3.14159//EN" or similar */
  1327.  
  1328.     if ( c == '"' || c == '\'' )
  1329.     {
  1330.         status = ParseString(doc, option);
  1331.         if (status)
  1332.             SetOptionInt( doc, TidyDoctypeMode, TidyDoctypeUser );
  1333.  
  1334.         return status;
  1335.     }
  1336.  
  1337.     /* read first word */
  1338.     while ( i < sizeof(buf)-1 && c != EndOfStream && !IsWhite(c) )
  1339.     {
  1340.         buf[i++] = (tmbchar) c;
  1341.         c = AdvanceChar( cfg );
  1342.     }
  1343.     buf[i] = '\0';
  1344.  
  1345.     if ( tmbstrcasecmp(buf, "auto") == 0 )
  1346.         dtmode = TidyDoctypeAuto;
  1347.     else if ( tmbstrcasecmp(buf, "omit") == 0 )
  1348.         dtmode = TidyDoctypeOmit;
  1349.     else if ( tmbstrcasecmp(buf, "strict") == 0 )
  1350.         dtmode = TidyDoctypeStrict;
  1351.     else if ( tmbstrcasecmp(buf, "loose") == 0 ||
  1352.               tmbstrcasecmp(buf, "transitional") == 0 )
  1353.         dtmode = TidyDoctypeLoose;
  1354.     else
  1355.     {
  1356.         ReportBadArgument( doc, option->name );
  1357.         status = no;
  1358.     }
  1359.      
  1360.     if ( status )
  1361.         SetOptionInt( doc, TidyDoctypeMode, dtmode );
  1362.     return status;
  1363. }
  1364.  
  1365. Bool ParseRepeatAttr( TidyDocImpl* doc, const TidyOptionImpl* option )
  1366. {
  1367.     Bool status = yes;
  1368.     tmbchar buf[64] = {0};
  1369.     int i = 0;
  1370.  
  1371.     TidyConfigImpl* cfg = &doc->config;
  1372.     tchar c = SkipWhite( cfg );
  1373.  
  1374.     while (i < sizeof(buf)-1 && c != EndOfStream && !IsWhite(c))
  1375.     {
  1376.         buf[i++] = (tmbchar) c;
  1377.         c = AdvanceChar( cfg );
  1378.     }
  1379.     buf[i] = '\0';
  1380.  
  1381.     if ( tmbstrcasecmp(buf, "keep-first") == 0 )
  1382.         cfg->value[ TidyDuplicateAttrs ] = TidyKeepFirst;
  1383.     else if ( tmbstrcasecmp(buf, "keep-last") == 0 )
  1384.         cfg->value[ TidyDuplicateAttrs ] = TidyKeepLast;
  1385.     else
  1386.     {
  1387.         ReportBadArgument( doc, option->name );
  1388.         status = no;
  1389.     }
  1390.     return status;
  1391. }
  1392.  
  1393. #if SUPPORT_UTF16_ENCODINGS
  1394. Bool ParseBOM( TidyDocImpl* doc, const TidyOptionImpl* option )
  1395. {
  1396.     ulong flag = 0;
  1397.     Bool status = ParseTriState( TidyAutoState, doc, option, &flag );
  1398.     if ( status )
  1399.     {
  1400.         SetOptionInt( doc, TidyOutputBOM, flag );
  1401.     }
  1402.     return status;
  1403. }
  1404. #endif
  1405.  
  1406. /* Use TidyOptionId as iterator.
  1407. ** Send index of 1st option after TidyOptionUnknown as start of list.
  1408. */
  1409. TidyIterator getOptionList( TidyDocImpl* doc )
  1410. {
  1411. #pragma unused(doc)
  1412.   return (TidyIterator) 1;
  1413. }
  1414.  
  1415. /* Check if this item is last valid option.
  1416. ** If so, zero out iterator.
  1417. */
  1418. const TidyOptionImpl*  getNextOption( TidyDocImpl* doc, TidyIterator* iter )
  1419. {
  1420. #pragma unused(doc)
  1421.  
  1422.   const TidyOptionImpl* option = NULL;
  1423. /*  TidyOptionId optId; */
  1424.   int optId;
  1425.   assert( iter != NULL );
  1426.   optId = *(TidyOptionId *) iter;
  1427.   if ( optId > TidyUnknownOption && optId < N_TIDY_OPTIONS )
  1428.   {
  1429.     option = &option_defs[ optId ];
  1430.     optId++;
  1431.   }
  1432.   *iter = (TidyIterator) ( optId < N_TIDY_OPTIONS ? (TidyIterator) optId : 0 );
  1433.   return option;
  1434. }
  1435.  
  1436. /* Use a 1-based array index as iterator: 0 == end-of-list
  1437. */
  1438. TidyIterator getOptionPickList( const TidyOptionImpl* option )
  1439. {
  1440.     ulong ix = 0;
  1441.     if ( option && option->pickList )
  1442.         ix = 1;
  1443.     return (TidyIterator) ix;
  1444. }
  1445.  
  1446. ctmbstr      getNextOptionPick( const TidyOptionImpl* option,
  1447.                                 TidyIterator* iter )
  1448. {
  1449.     ulong ix;
  1450.     ctmbstr val = NULL;
  1451.     assert( option!=NULL && iter != NULL );
  1452.  
  1453.     ix = (ulong) *iter;
  1454.     if ( ix > 0 && ix < 16 && option->pickList )
  1455.         val = option->pickList[ ix-1 ];
  1456.     *iter = (TidyIterator) ( val && option->pickList[ix] ? ix + 1 : 0 );
  1457.     return val;
  1458. }
  1459.  
  1460. static int  WriteOptionString( const TidyOptionImpl* option,
  1461.                                ctmbstr sval, StreamOut* out )
  1462. {
  1463.   ctmbstr cp = option->name;
  1464.   while ( *cp )
  1465.       WriteChar( *cp++, out );
  1466.   WriteChar( ':', out );
  1467.   WriteChar( ' ', out );
  1468.   cp = sval;
  1469.   while ( *cp )
  1470.       WriteChar( *cp++, out );
  1471.   WriteChar( '\n', out );
  1472.   return 0;
  1473. }
  1474.  
  1475. static int  WriteOptionInt( const TidyOptionImpl* option, uint ival, StreamOut* out )
  1476. {
  1477.   tmbchar sval[ 32 ] = {0};
  1478.   tmbsnprintf(sval, sizeof(sval), "%d", ival );
  1479.   return WriteOptionString( option, sval, out );
  1480. }
  1481.  
  1482. static int  WriteOptionBool( const TidyOptionImpl* option, Bool bval, StreamOut* out )
  1483. {
  1484.   ctmbstr sval = bval ? "yes" : "no";
  1485.   return WriteOptionString( option, sval, out );
  1486. }
  1487.  
  1488. static int  WriteOptionPick( const TidyOptionImpl* option, uint ival, StreamOut* out )
  1489. {
  1490.     uint ix;
  1491.     const ctmbstr* val = option->pickList;
  1492.     for ( ix=0; val[ix] && ix<ival; ++ix )
  1493.         /**/;
  1494.     if ( ix==ival && val[ix] )
  1495.         return WriteOptionString( option, val[ix], out );
  1496.     return -1;
  1497. }
  1498.  
  1499. Bool  ConfigDiffThanSnapshot( TidyDocImpl* doc )
  1500. {
  1501.   int diff = memcmp( &doc->config.value, &doc->config.snapshot,
  1502.                      N_TIDY_OPTIONS * sizeof(uint) );
  1503.   return ( diff != 0 );
  1504. }
  1505.  
  1506. Bool  ConfigDiffThanDefault( TidyDocImpl* doc )
  1507. {
  1508.   Bool diff = no;
  1509.   const TidyOptionImpl* option = option_defs + 1;
  1510.   ulong* ival = doc->config.value;
  1511.   for ( /**/; !diff && option && option->name; ++option, ++ival )
  1512.   {
  1513.     diff = ( *ival != option->dflt );
  1514.   }
  1515.   return diff;
  1516. }
  1517.  
  1518.  
  1519. static int  SaveConfigToStream( TidyDocImpl* doc, StreamOut* out )
  1520. {
  1521.     int rc = 0;
  1522.     const TidyOptionImpl* option;
  1523.     for ( option=option_defs+1; 0==rc && option && option->name; ++option )
  1524.     {
  1525.         ulong ival = doc->config.value[ option->id ];
  1526.         if ( option->parser == NULL )
  1527.             continue;
  1528.         if ( ival == option->dflt && option->id != TidyDoctype)
  1529.             continue;
  1530.  
  1531.         if ( option->id == TidyDoctype )  /* Special case */
  1532.         {
  1533.           ulong dtmode = cfg( doc, TidyDoctypeMode );
  1534.           if ( dtmode == TidyDoctypeUser )
  1535.           {
  1536.             tmbstr t;
  1537.             
  1538.             /* add 2 double quotes */
  1539.             if (( t = (tmbstr)MemAlloc( tmbstrlen( (ctmbstr)ival) + 2 ) ))
  1540.             {
  1541.               t[0] = '\"'; t[1] = 0;
  1542.             
  1543.               tmbstrcat( t, (ctmbstr)ival );
  1544.               tmbstrcat( t, "\"" );
  1545.               rc = WriteOptionString( option, (ctmbstr)t, out );
  1546.             
  1547.               MemFree( t );
  1548.             }
  1549.           }
  1550.           else if ( dtmode == option_defs[TidyDoctypeMode].dflt )
  1551.             continue;
  1552.           else
  1553.             rc = WriteOptionPick( option, dtmode, out );
  1554.         }
  1555.         else if ( option->pickList )
  1556.           rc = WriteOptionPick( option, ival, out );
  1557.         else
  1558.         {
  1559.           switch ( option->type )
  1560.           {
  1561.           case TidyString:
  1562.             rc = WriteOptionString( option, (ctmbstr) ival, out );
  1563.             break;
  1564.           case TidyInteger:
  1565.             rc = WriteOptionInt( option, ival, out );
  1566.             break;
  1567.           case TidyBoolean:
  1568.             rc = WriteOptionBool( option, ival ? yes : no, out );
  1569.             break;
  1570.           }
  1571.         }
  1572.     }
  1573.     return rc;
  1574. }
  1575.  
  1576. int  SaveConfigFile( TidyDocImpl* doc, ctmbstr cfgfil )
  1577. {
  1578.     int status = -1;
  1579.     StreamOut* out = NULL;
  1580.     uint outenc = cfg( doc, TidyOutCharEncoding );
  1581.     uint nl = cfg( doc, TidyNewline );
  1582.     FILE* fout = fopen( cfgfil, "wb" );
  1583.     if ( fout )
  1584.     {
  1585.         out = FileOutput( fout, outenc, nl );
  1586.         status = SaveConfigToStream( doc, out );
  1587.         fclose( fout );
  1588.         MemFree( out );
  1589.     }
  1590.     return status;
  1591. }
  1592.  
  1593. int  SaveConfigSink( TidyDocImpl* doc, TidyOutputSink* sink )
  1594. {
  1595.     uint outenc = cfg( doc, TidyOutCharEncoding );
  1596.     uint nl = cfg( doc, TidyNewline );
  1597.     StreamOut* out = UserOutput( sink, outenc, nl );
  1598.     int status = SaveConfigToStream( doc, out );
  1599.     MemFree( out );
  1600.     return status;
  1601. }
  1602.